package com.tuzhi.auth.sys.scurity.impl;

import com.tuzhi.auth.sys.scurity.TokenStoreService;
import com.tuzhi.auth.sys.scurity.exception.LoginUserException;
import com.tuzhi.base.web.util.AddressUtils;
import com.tuzhi.base.web.util.IpUtil;
import com.tuzhi.base.web.util.ServletUtil;
import com.tuzhi.common.constants.security.Constants;
import com.tuzhi.common.domain.JwtPayload;
import com.tuzhi.common.domain.LoginUserBO;
import com.tuzhi.util.JwtUtil;
import eu.bitwalker.useragentutils.UserAgent;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import static com.tuzhi.common.constants.security.Constants.AUTHORIZE_TOKEN;

/**
 * token验证处理
 */

@Slf4j
@Service
public class TokenServiceImpl {
    // 令牌自定义标识
//    @Value("${token.header:Authorization}")
//    private String header;

    // 令牌秘钥
    @Value("${token.secret:jwttuzhihygamwabcdefghijkltuzhi517465mnopqrstuvwxyztuzhihygamw20231223dxjwttuzhihygamw}")
    private String secret;

    // 令牌有效期（默认30分钟）
    @Value("${token.expireTime:30}")
    private int expireTime;

    protected static final long MILLIS_SECOND = 1000;

    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

    private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;

    /**
     * 9小时毫秒数
     */
    private static final long MILLIS_SECOND_15 = 1000 * 60 * 60 * 9;

    @Autowired
    private TokenStoreService tokenStoreService;


    public LoginUserBO getLoginUserNullErr(HttpServletRequest request) {
        LoginUserBO loginUser = getLoginUser(request);
        if (loginUser == null) {
            throw new LoginUserException("获取用户登录信息失败");
        }
        return loginUser;
    }

    public LoginUserBO createLoginUser(String userId, HttpServletRequest request) {
        LoginUserBO loginUserBO = new LoginUserBO(userId);
        setUserAgent(loginUserBO);
        return loginUserBO;
    }

    public LoginUserBO getCurUser() {
        return getLoginUser(ServletUtil.getRequest());
    }


    /**
     * 获取用户身份信息
     *
     * @return 用户信息
     */
    public LoginUserBO getLoginUser(HttpServletRequest request) {
        // 获取请求携带的令牌
        String token = getToken(request);
        return getLoginUser(token);
    }

    /**
     * 获取用户身份信息
     *
     * @return 用户信息
     */
    public LoginUserBO getLoginUser(String token) {
        if (StringUtils.isNotEmpty(token)) {
            try {
                Claims claims = parseToken(token);
                // 解析对应的权限以及用户信息
                String uuid = claims.getId();
                String userKey = getTokenKey(uuid);
                //这个tokenStoreService，在现在用的是tuzhi-auth-sys 中的 MyTokenStoreServiceImpl
                LoginUserBO user = tokenStoreService.getLoginUser(userKey);
                return user;
            } catch (Exception e) {
                log.error("getLoginUser err:{}", ExceptionUtils.getStackTrace(e));
            }
        }
        return null;
    }

    /**
     * 设置用户身份信息
     */
    public void setLoginUser(LoginUserBO loginUser) {
        if (loginUser != null && StringUtils.isNotEmpty(loginUser.getJti())) {
            saveToken(loginUser);
        }
    }

    /**
     * 删除用户身份信息
     */
    public void delLoginUser(String uuid) {
        if (StringUtils.isNotEmpty(uuid)) {
            String userKey = getTokenKey(uuid);
            tokenStoreService.delLoginUser(userKey);
        }
    }

    /**
     * 创建令牌
     *
     * @param loginUser 用户信息
     * @return 令牌
     */
    public String createToken(LoginUserBO loginUser) {
        String uuid = UUID.randomUUID().toString();
        loginUser.setJti(uuid);
        setUserAgent(loginUser);
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
        String token = buildJwt(loginUser);
//        loginUser.setToken(token);
        setLoginUser(loginUser);
        return token;
    }

    /**
     * 根据用户信息LoginUserBO，构建一个Json Web Token
     *
     * @param loginUser
     * @return
     */
    public String buildJwt(LoginUserBO loginUser) {
        JwtPayload jwtPayload = new JwtPayload(loginUser.getJti(), "tuzhi", expireTime, TimeUnit.MINUTES);
        String token = JwtUtil.createJWT(secret, jwtPayload);
        return token;
    }


    /**
     * 验证令牌有效期，相差不足20分钟，自动刷新缓存
     *
     * @param loginUser
     * @return 令牌
     */
    public void verifyToken(LoginUserBO loginUser) {
        long expireTime = loginUser.getExpireTime();
        long currentTime = System.currentTimeMillis();
        if (expireTime - currentTime <= MILLIS_MINUTE_TEN) {
            saveToken(loginUser);
        }
    }

    public boolean verifyLoginUserBO(LoginUserBO loginUserBO) {
        if (loginUserBO != null) {
            boolean flag = loginUserBO.getExpireTime() > System.currentTimeMillis();
            return flag;
        }
        return false;
    }

    /**
     * 保存令牌有效期
     *
     * @param loginUser 登录信息
     */
    public void saveToken(LoginUserBO loginUser) {
        tokenStoreService.storeLoginUser(loginUser);
        log.info("保存 TOKEN:{},expireTIME:{}", loginUser.getJti(), expireTime);
    }

    /**
     * 刷新令牌有效期
     *
     * @param loginUser 登录信息
     */
    public String refreshToken(LoginUserBO loginUser) {
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
        String token = buildJwt(loginUser);
//        loginUser.setToken(token);
        saveToken(loginUser);
        log.info("刷新 TOKEN:{},expireTIME:{}", loginUser.getJti(), expireTime);
        return token;
    }


    /**
     * 根据旧token获取token：
     * 1.如果token失效了，则返回null
     * 2.如果token还有效，有效期<=15分钟
     * 2.其他(如果token还有效，有效期>15分钟)，
     *
     * @param token 旧token
     */
    public String tokenIsExpRefresh(String token) {
        token = getToken(token);
        LoginUserBO loginUserBO = getLoginUser(token);
        if (loginUserBO == null) {
            return null;
        }
        long currentTime = System.currentTimeMillis();
        long ttl = loginUserBO.getExpireTime() - currentTime;
        if (ttl <= MILLIS_SECOND_15) {
            return refreshToken(loginUserBO);
        } else {
            return token;
        }
    }

    public static ServletRequestAttributes getRequestAttributes() {
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        return (ServletRequestAttributes) attributes;
    }

    public static HttpServletRequest getRequest() {
        return getRequestAttributes().getRequest();
    }

    /**
     * 设置用户代理信息
     *
     * @param loginUser 登录信息
     */
    public void setUserAgent(LoginUserBO loginUser) {
        UserAgent userAgent = UserAgent.parseUserAgentString(getRequest().getHeader("User-Agent"));
        String ip = IpUtil.getIpAddr(getRequest());
        loginUser.setIpaddr(ip);
        loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
        loginUser.setBrowser(userAgent.getBrowser().getName());
        loginUser.setOs(userAgent.getOperatingSystem().getName());

    }

    /**
     * 从令牌中获取数据声明
     *
     * @param token 令牌
     * @return 数据声明
     */
    private Claims parseToken(String token) {
        return JwtUtil.parseJWT(secret, token);
    }

    /**
     * 从令牌中获取用户名
     *
     * @param token 令牌
     * @return 用户名
     */
    public String getUsernameFromToken(String token) {
        Claims claims = parseToken(token);
        return claims.getSubject();
    }

    public static void main(String[] args) {
        String uuid = UUID.randomUUID().toString();
        System.out.println(uuid);
       /* TokenServiceImpl tokenService = new TokenServiceImpl();
        String k = tokenService.getUsernameFromToken("eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImZlNDRjMmY3LWEzNWItNDUxNC04OTI2LTU3Zjc0MmNkZDg2OCJ9.p2JedhyRR5Oa_LPfMgFP3L9pZYFNxWBU-eYqZba5HmAEWMIsAe00hR-Sp5uw1aeqQPwPWhWZ1enc01pWKGYBlg");
        System.out.printf(k);*/
    }

    /**
     * 获取请求token
     *
     * @param request
     * @return token
     */
    public String getToken(HttpServletRequest request) {
        String token = request.getHeader(AUTHORIZE_TOKEN);
        return getToken(token);
    }

    public String getToken(String token) {
        if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
            token = token.replace(Constants.TOKEN_PREFIX, "");
        }
        return token;
    }

    public String getTokenKey(String uuid) {
        return tokenStoreService.getTokenKey(uuid);
    }

}
